/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.swing.control; import java.awt.datatransfer.Transferable; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.util.List; import java.util.ResourceBundle; import javax.swing.JList; /** * A sub-class of JList that supports drag and drop to reorder items within a single list * and to copy or move items between lists. It is used by the * {@linkplain org.geotools.swing.MapLayerTable} widget but has been written * in a general fashion so that it may be used for other pusposes. * * @see DnDListModel * * @author Michael Bedward * @since 2.6 * * @source $URL$ * @version $Id$ */ public class DnDList<T> extends JList implements DragGestureListener, DragSourceListener, DropTargetListener { private static final long serialVersionUID = 3310751294076288683L; private static final ResourceBundle stringRes = ResourceBundle.getBundle("org/geotools/swing/Text"); private DragSource src; @SuppressWarnings("unused") private DropTarget tgt; // this is not used? what is it for private boolean movingItems; private int overIndex; private int[] dragIndices; /** * Default constructor. An DnDListModel object will be created * for the list. */ public DnDList() { this(new DnDListModel<T>()); } /** * Constructor allowing the list model to be specified * @param model an instance of DnDListModel * @throws IllegalArgumentException if model is null */ public DnDList(DnDListModel<T> model) { super( model ); if (model == null) { throw new IllegalArgumentException(stringRes.getString("arg_null_error")); } //this.setModel(model); src = new DragSource(); src.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); tgt = new DropTarget(this, this); movingItems = false; overIndex = -1; } @SuppressWarnings("unchecked") @Override public DnDListModel<T> getModel() { return (DnDListModel<T>) super.getModel(); } /** * Called by the system when a drag gesture starts */ public void dragGestureRecognized(DragGestureEvent dge) { dragIndices = getSelectedIndices(); if (dragIndices.length > 0) { List<T> items = getModel().getElementsAt(dragIndices); Transferable stuff = new DnDListItemsTransferable<T>(items); movingItems = true; src.startDrag(dge, DragSource.DefaultMoveDrop, stuff, this); } } /** * DragSourceListener method - presently ignored * <p> * Description copied from interface:<br> * Called as the cursor's hotspot enters a platform-dependent drop site. * This method is invoked when all the following conditions are true: * <ul> * <li> The cursor's hotspot enters the operable part of a platform- dependent drop site. * <li> The drop site is active. * <li> The drop site accepts the drag. * </ul> */ public void dragEnter(DragSourceDragEvent dsde) { // do nothing } /** * DragSourceListener method - presently ignored * <p> * Description copied from interface:<br> * Called as the cursor's hotspot moves over a platform-dependent drop site. * This method is invoked when all the following conditions are true: * <ul> * <li> The cursor's hotspot has moved, but still intersects the operable part * of the drop site associated with the previous dragEnter() invocation. * <li> The drop site is still active. * <li> The drop site accepts the drag. * </ul> */ public void dragOver(DragSourceDragEvent dsde) { // do nothing } /** * DragSourceListener method - presently ignored * <p> * Description copied from interface:<br> * Called when the user has modified the drop gesture. This method is invoked when * the state of the input device(s) that the user is interacting with changes. * Such devices are typically the mouse buttons or keyboard modifiers that the * user is interacting with. */ public void dropActionChanged(DragSourceDragEvent dsde) { // do nothing } /** * DragSourceListener method - presently ignored * <p> * Description copied from interface:<br> * Called as the cursor's hotspot exits a platform-dependent drop site. * This method is invoked when <b>any</b> of the following conditions are true: * <ul> * <li> The cursor's hotspot no longer intersects the operable part of the * drop site associated with the previous dragEnter() invocation. * <li> The drop site associated with the previous dragEnter() invocation is * no longer active. * <li> The drop site associated with the previous dragEnter() invocation * has rejected the drag. * </ul> */ public void dragExit(DragSourceEvent dse) { // do nothing } /** * Description copied from interface:<br> * This method is invoked to signify that the Drag and Drop operation is * complete. The getDropSuccess() method of the DragSourceDropEvent can be * used to determine the termination state. The getDropAction() method returns * the operation that the drop site selected to apply to the Drop operation. * Once this method is complete, the current DragSourceContext and associated * resources become invalid. */ public void dragDropEnd(DragSourceDropEvent dsde) { movingItems = false; overIndex = -1; } /** * Records the index of the list item (if any) pointed to by the mouse cursor * <p> * Description copied from interface:<br> * Called while a drag operation is ongoing, when the mouse pointer enters * the operable part of the drop site for the DropTarget registered with * this listener. */ public void dragEnter(DropTargetDragEvent dtde) { overIndex = this.locationToIndex(dtde.getLocation()); setSelectedIndex(overIndex); } /** * Records the index of the list item (if any) pointed to by the mouse cursor * <p> * Description copied from interface:<br> * Called when a drag operation is ongoing, while the mouse pointer is still * over the operable part of the drop site for the DropTarget registered with * this listener. */ public void dragOver(DropTargetDragEvent dtde) { int index = this.locationToIndex(dtde.getLocation()); if (index >= 0 && index != overIndex) { overIndex = index; setSelectedIndex(overIndex); } } /** * DropTargetListener method - presently ignored * <p> * Description copied from interface:<br> * Called if the user has modified the current drop gesture. */ public void dropActionChanged(DropTargetDragEvent dtde) { // do nothing } /** * Description copied from interface:<br> * Called while a drag operation is ongoing, when the mouse pointer has exited * the operable part of the drop site for the DropTarget registered with this * listener. */ public void dragExit(DropTargetEvent dte) { overIndex = -1; } /** * Handles the moving (for drag and drop actions within this list) or transfer * (for actions between lists) of list items. * Description copied from interface:<br> * Called when the drag operation has terminated with a drop on the operable part * of the drop site for the DropTarget registered with this listener. * <p> * This method is responsible for undertaking the transfer of the data associated * with the gesture. The DropTargetDropEvent provides a means to obtain a Transferable * object that represents the data object(s) to be transfered. * <p> * From this method, the DropTargetListener shall accept or reject the drop via * the acceptDrop(int dropAction) or rejectDrop() methods of the DropTargetDropEvent * parameter. * <p> * Subsequent to acceptDrop(), but not before, DropTargetDropEvent's getTransferable() * method may be invoked, and data transfer may be performed via the returned * Transferable's getTransferData() method. * <p> * At the completion of a drop, an implementation of this method is required to * signal the success/failure of the drop by passing an appropriate boolean to the * DropTargetDropEvent's dropComplete(boolean success) method. * <p> * Note: The data transfer should be completed before the call to the * DropTargetDropEvent's dropComplete(boolean success) method. After that, a call * to the getTransferData() method of the Transferable returned by * DropTargetDropEvent.getTransferable() is guaranteed to succeed only if the data * transfer is local; that is, only if DropTargetDropEvent.isLocalTransfer() * returns true. Otherwise, the behavior of the call is implementation-dependent. */ public void drop(DropTargetDropEvent dtde) { //Transferable stuff = dtde.getTransferable(); /* * @todo check DataFlavor of stuff */ dtde.acceptDrop(DnDConstants.ACTION_MOVE); if (movingItems) { /* * This is a local drag and drop to reorder items */ DnDListModel<T> model = getModel(); model.moveItems(overIndex, dragIndices); } else { // @todo stuff dragged from other list } overIndex = -1; movingItems = false; dtde.getDropTargetContext().dropComplete(true); } }